package com.thunbu.thunbu_meeting_system.rfid;

import android.util.Log;

import androidx.annotation.Nullable;

import com.honeywell.rfidservice.rfid.Gen2;
import com.honeywell.rfidservice.rfid.RfidReader;
import com.honeywell.rfidservice.rfid.RfidReaderException;
import com.thunbu.thunbu_meeting_system.utils.SpUtils;


import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;

public class WorkMode {
    public static final String KEY_SCENARIO = "KEY_SCENARIO";
    public static final String KEY_RFID_TARGET_INTERVAL = "KEY_SCENARIO_TARGET_INTERVAL";

    private final String PRE_CUSTOM_SCENARIO = "custome_scenario";
    private final static String TAG = "WorkMode";

    public static final String KEY_PROFILEID = "key_profileid";
    public static final String KEY_SESSION = "key_session";
    public static final String KEY_QVALUE = "key_qvalue";
    public static final String KEY_TARGET = "key_target";
    public static final String KEY_MAX_Q = "key_max_q";
    public static final String KEY_MIN_Q = "key_min_q";
    public static final String KEY_QUERY_CYCLE = "key_query_cycle";

    public static final int Q_AUTO = 0;

    private Modes mCurrentScenario = Modes.MODE_ANTI_INTERFERENCE;
    private Modes mCustomMode = Modes.MODE_CUSTOM;

    private HashMap<Integer, JSONObject> mModes;

    private static class NormalBuilder {
        private static final WorkMode mInstance;
        static {
            mInstance = new WorkMode(false);
        }
    }

    private static class HonBuilder {
        private static final WorkMode mInstance;
        static {
            mInstance = new WorkMode(true);
        }
    }

    public static WorkMode getInstance(boolean isHonReader) {
        if (isHonReader) {
            return HonBuilder.mInstance;
        }
        return NormalBuilder.mInstance;
    }

    private WorkMode(boolean isHonReader) {
        init(isHonReader);
    }

    private void init(boolean isHonReader) {
        mModes = new HashMap<>();
        for (Modes mode : Modes.values()) {
            if (Modes.MODE_CUSTOM == mode) {
                mode = mCustomMode;
            }
            JSONObject object = createMode(mode, isHonReader);
            mModes.put(mode.value(), object);
        }
        mCurrentScenario = Modes.valueOf(Integer.parseInt(
                SpUtils.getString(KEY_SCENARIO,
                        "" + Modes.MODE_ANTI_INTERFERENCE)));
    }

    public static class Mode {
        int session;
        int target;
        int QValue;
        int profileID;
        Integer maxQ;
        Integer minQ;
        Integer queryCycle;

        Mode(int session, int target, int QValue, int profileID) {
            this(session, target, QValue, profileID, null, null, null);
        }

        Mode(int session, int target, int QValue, int profileID,
             Integer maxQ, Integer minQ, Integer queryCycle) {
            this.session = session;
            this.target = target;
            this.QValue = QValue;
            this.profileID = profileID;
            this.maxQ = maxQ;
            this.minQ = minQ;
            this.queryCycle = queryCycle;
        }

        public void setSession(int session) {
            this.session = session;
        }

        public void setTarget(int target) {
            this.target = target;
        }

        public void setQValue(int QValue) {
            this.QValue = QValue;
        }

        public void setProfileID(int profileID) {
            this.profileID = profileID;
        }

        public void setMaxQ(Integer maxQ) {
            this.maxQ = maxQ;
        }

        public void setMinQ(Integer minQ) {
            this.minQ = minQ;
        }

        public void setQueryCycle(Integer queryCycle) {
            this.queryCycle = queryCycle;
        }

        public int getSession() {
            return session;
        }

        public int getTarget() {
            return target;
        }

        public int getQValue() {
            return QValue;
        }

        public int getProfileID() {
            return profileID;
        }

        public Integer getMaxQ() {
            return maxQ;
        }

        public Integer getMinQ() {
            return minQ;
        }

        public Integer getQueryCycle() {
            return queryCycle;
        }
    }

    public enum Modes {
        MODE_ANTI_INTERFERENCE(1001,
                new Mode(0, 2, Q_AUTO, 1),  // target: 0 A, 1 B, 2 A->B, 3 B->A
                new Mode(0, 2, Q_AUTO, 7)),  // target: 0 A, 1 B, 2 A<->B
        MODE_MULTI_TAG_INVENTORY(1002,
                new Mode(1, 0, Q_AUTO, 1),
                new Mode(1, 0, Q_AUTO, 3)),
        MODE_MASS_TAG_SINGLE_INVENTORY(1003,
                new Mode(2, 0, Q_AUTO, 1),
                new Mode(2, 0, Q_AUTO, 3)),
        MODE_MASS_TAG_MULTI_INVENTORY(1004,  // Need manual switch the target from A->B or B->A
                new Mode(2, 0, Q_AUTO, 1),
                new Mode(2, 0, Q_AUTO, 3)),
        MODE_SINGLE_FAST(1005,
                new Mode(0, 2, 1, 3),
                new Mode(0, 2, 1, 5)),
        MODE_MULTI_FAST(1006,
                new Mode(0, 2, Q_AUTO, 3),
                new Mode(0, 2, Q_AUTO, 5)),
        MODE_CUSTOM(1007,
                new Mode(1, 0, Q_AUTO, 1),
                new Mode(1, 0, Q_AUTO, 3));
        private final int value;
        private final Mode modeNormal;
        private final Mode modeHon;
        Modes(int value, Mode modeNormal, Mode modeHon) {
            this.value = value;
            this.modeNormal = modeNormal;
            this.modeHon = modeHon;
        }
        public int value() {
            return value;
        }
        public Mode getModeNormal() {
            return modeNormal;
        }
        public Mode getModeHon() {
            return modeHon;
        }
        public static Modes valueOf(int value) {
            for (Modes mode : Modes.values()) {
                if (value == mode.value()) {
                    return mode;
                }
            }
            return Modes.MODE_ANTI_INTERFERENCE;
        }
        @Override
        public String toString() {
            return String.valueOf(value);
        }
    }

    private JSONObject createMode(Modes mode, boolean isHonReader) {
        Mode mode_;
        if (isHonReader) {
            mode_ = mode.getModeHon();
        } else {
            mode_ = mode.getModeNormal();
        }
        if (Modes.MODE_CUSTOM == mode) {  // Get the custom value from SharedPreference
            syncModeFromJson(readCustomMode(),
                    new Mode[] {mode.getModeHon(), mode.getModeNormal()});
        }
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put(KEY_PROFILEID, mode_.getProfileID());
            jsonObject.put(KEY_TARGET, mode_.getTarget());
            jsonObject.put(KEY_QVALUE, mode_.getQValue());
            jsonObject.put(KEY_SESSION, mode_.getSession());
            if (null != mode_.getMaxQ()) {
                jsonObject.put(KEY_MAX_Q, mode_.getMaxQ());
            }
            if (null != mode_.getMinQ()) {
                jsonObject.put(KEY_MIN_Q, mode_.getMinQ());
            }
            if (null != mode_.getQueryCycle()) {
                jsonObject.put(KEY_QUERY_CYCLE, mode_.getQueryCycle());
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    public boolean enableMode(int mode, RfidReader rfidReader) {
        if (Modes.MODE_CUSTOM.value() == mode) {
            return enableMode(mCustomMode, rfidReader);
        }
        return enableMode(Modes.valueOf(mode), rfidReader);
    }

    public boolean enableMode(Modes mode, RfidReader rfidReader) {
        JSONObject modeObj = mModes.get(mode.value());
        if (null == modeObj) {
            return false;
        }
        Log.i(TAG, "enableMode Mode =" + modeObj.toString() + " mode=" + mode);
        mCurrentScenario = mode;
        if (null == rfidReader) {
            return false;
        }
        Mode mode_;
        if (rfidReader.isHonReader()) {
            mode_ = mode.getModeHon();
        } else {
            mode_ = mode.getModeNormal();
        }
        try {
            Gen2.Session session = Gen2.Session.Session0;
            switch (mode_.getSession()) {
                case 0:
                    session = Gen2.Session.Session0;
                    break;
                case 1:
                    session = Gen2.Session.Session1;
                    break;
                case 2:
                    session = Gen2.Session.Session2;
                    break;
                case 3:
                    session = Gen2.Session.Session3;
                    break;
            }
            int target = mode_.getTarget();
            int profile = mode_.getProfileID();
            int Q = mode_.getQValue() - 1;  // Auto value is -1 ,minus 1

            if (rfidReader.isHonReader()) {
                com.honeywell.rfidservice.rfid.WorkMode workMode =
                        new com.honeywell.rfidservice.rfid.WorkMode();
                workMode.session = session;
                workMode.target = target;
                workMode.profileId = profile;
                workMode.Q = Q;
                if (null != mode_.getMaxQ()) {
                    workMode.maxQ = mode_.getMaxQ();
                }
                if (null != mode_.getMinQ()) {
                    workMode.minQ = mode_.getMinQ();
                }
                if (null != mode_.getQueryCycle()) {
                    workMode.queryCycle = mode_.getQueryCycle();
                }
                return rfidReader.setWorkMode(workMode);
            }

            rfidReader.setTarget(target);
            rfidReader.setProfile(profile);
            rfidReader.setSession(session);
            rfidReader.setQ(Q);
//                if(mode==MODE_MULTI_TAG_INVENTORY){
//                    setAntPower(rfidReader,2700);
//                }else{
//                    setAntPower(rfidReader,3000);
//                }
            return true;
        } catch (RfidReaderException e) {
            e.printStackTrace();
        }
        return false;
    }

    public JSONObject getMode(int id) {
        return mModes.get(id);
    }

    public int getCurrentScenarioId() {
        return mCurrentScenario.value();
    }

    public JSONObject getCurrentScenario() {
        return mModes.get(mCurrentScenario.value());
    }

    private boolean syncModeFromJson(@Nullable JSONObject obj, Mode[] modes) {
        if (null == obj) {
            return false;
        }
        try {
            int session = obj.getInt(KEY_SESSION);
            int target = obj.getInt(KEY_TARGET);
            int QValue = obj.getInt(KEY_QVALUE);
            int profileID = obj.getInt(KEY_PROFILEID);
            Integer maxQ = null;
            Integer minQ = null;
            Integer queryCycle = null;
            if (obj.has(KEY_MAX_Q)) {
                maxQ = obj.getInt(KEY_MAX_Q);
            }
            if (obj.has(KEY_MIN_Q)) {
                minQ = obj.getInt(KEY_MIN_Q);
            }
            if (obj.has(KEY_QUERY_CYCLE)) {
                queryCycle = obj.getInt(KEY_QUERY_CYCLE);
            }
            for (Mode mode : modes) {
                mode.setSession(session);
                mode.setTarget(target);
                mode.setQValue(QValue);
                mode.setProfileID(profileID);
                mode.setMaxQ(maxQ);
                mode.setMinQ(minQ);
                mode.setQueryCycle(queryCycle);
            }
            return true;
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Save the custom mode, the value changed should be processed
     * to the return value of {@link WorkMode#getMode}
     * @param reader The reader
     */
    public void saveCustomMode(RfidReader reader) {
        if (mCurrentScenario == Modes.MODE_CUSTOM && reader != null) {
            Mode mode = reader.isHonReader() ?
                    mCurrentScenario.getModeHon() : mCurrentScenario.getModeNormal();
            if (syncModeFromJson(mModes.get(Modes.MODE_CUSTOM.value()), new Mode[] {mode})) {
                if (!enableMode(mCurrentScenario, reader)) {
                    // Failure
                    return;
                }
            }
        }
        SpUtils.putString(PRE_CUSTOM_SCENARIO, getMode(Modes.MODE_CUSTOM.value()).toString());
    }

    private JSONObject readCustomMode() {
        String json = SpUtils.getString(PRE_CUSTOM_SCENARIO, null);
        if (json != null && json.length() > 0) {
            try {
                return new JSONObject(json);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
